/**
 * @class UI组件-项目列表
 * @author DuskyHuang 圣子
*/

// TODO 将组件有涉及onLoad赋值组件却提供update方法的onLoad提升到__preload，暂时更新ItemList
import { _decorator, Component, Node, Prefab, js, Enum } from 'cc';
import { IPoolable, ItemPool, serialize, tip } from '../utils';
const { ccclass } = _decorator;

enum ReduceType {
    Free,       // 释放回对象池
    Inactive    // 非激活状态
}

export interface __IItem<Data = any> extends IPoolable, Component {
    onDataUpdated(data?: Data, ...params: any[]): void;
    claimed?(...args: any[]): void;
    freed?(): void;
    free?(): void;
}

@ccclass('cfw.ItemList')
export class ItemList<Item extends __IItem> extends Component {

    public static readonly ReduceType = ReduceType;

    @tip("缩减方式", ["type", Enum(ReduceType)]) public reduceType: ReduceType = ReduceType.Free;

    @serialize private _container: Node = null;
    @tip("容器节点（默认自身）", Node)
    public get container(): Node { return this._container ?? this.node; }
    public set container(arg: Node) { this._container = arg; }

    @serialize private _prefab: Prefab = null;
    @tip("预制模板（Item类）", Prefab)
    public get itemPrefab(): Prefab { return this._prefab; }
    public set itemPrefab(arg: Prefab) {
        this._prefab = arg;
        if (!this.itemClassName) this.itemClassName = arg.data._name;
    }

    @tip("预制类名（Item类）") public itemClassName: string = '';

    private _pool: ItemPool<Item>;
    private _ItemClass: Constructor<Item>;

    public __preload(): void {
        if (!this.itemPrefab) return console.error('[ItemList.onLoad]', 'item prefab not defined');
        this._ItemClass = js.getClassByName(this.itemClassName) as Constructor<Item>;
        if (!this._ItemClass) return console.error('[ItemList.onLoad]:', `Item class "${this.itemClassName}" not found`);
        this._pool = new ItemPool(
            this._ItemClass,
            this.itemPrefab,
            this.container
        );
    }

    public onDestroy(): void {
        this._pool.clear();
    }

    public updateList(dataList: any[], ...args: any[]): void {
        if (!this._pool) return;
        const list = this.node.children;
        if (isEmpty(dataList)) {
            if (this.reduceType === ReduceType.Free) this.freeAll();
            else list.forEach(child => child.active = false);
            return;
        }
        const { length } = list, { length: size } = dataList;
        const max = Math.max(length, size);
        for (let i = 0; i < max; i++) {
            const node = list[i], data = dataList[i];
            if (node && data) {
                this._getItemOn(node).onDataUpdated(data, ...args);
                node.active = true;
            } else if (!node) {
                this._pool.claim(this._ItemClass).onDataUpdated(data, ...args);
            } else if (this.reduceType === ReduceType.Free) {
                this._pool.free(this._getItemOn(node));
            } else {
                node.active = false;
            }
        }
    }

    public get items(): Item[] {
        return this.container.children.map(child => this._getItemOn(child));
    }

    public freeAll(): void {
        if (this.container.children.length <= 0) return;
        [...this.container.children].forEach(node => this._pool.free(this._getItemOn(node)));
    }

    private _getItemOn(node: Node): Item {
        return (node as any).getComponent(this._ItemClass);
    }
}